//
//  ViewController.m
//  SerialPort
//
//  Created by MT-SW-003 on 2021/4/9.
//  Copyright © 2021 MT-SW-003. All rights reserved.
//

#import "ViewController.h"
#import "SWSerialPortController.h"
#import <ZHYYTool/ZHYYTool.h>


@implementation ViewController
{
    int GUIfd;
    id serialPortController;
    id checkedData;
    BOOL autoSaveFlag;
    NSNotificationCenter *center;
    NSString *autoSavePath;
    NSString *basePath;
    NSString *endMarkStr;
    BOOL isSendEndMarkStr;
    BOOL isLoopSend;
    NSString *timeInterval;
    BOOL isLoopSending;
}


/*
* Load view and init config of serial port.
*/
- (void)viewDidLoad {
    [super viewDidLoad];
    [self initGUIConfig];
    [self autoSearchDevice];
  
}


- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}

/*
* Open serial button.
*/
- (IBAction)openSerial:(id)sender {
    //open serial port
    GUIfd = [serialPortController openSerialPort:self.serialDevicePath.stringValue];
        
    //set serial port
    char *parity = (char *)[self.paritySelector.stringValue UTF8String];

    [serialPortController setSerialPortForDevice:GUIfd
                                    withBaudRate:[self.baudRateSelector.stringValue intValue]
                                     withDataBit:[self.dataBitSelector.stringValue intValue]
                                     withStopBit:[self.stopBitSelector.stringValue intValue]
                                      withParity:*parity];
         
    if(GUIfd<0){
        [self displayTextToTerminal:[NSString stringWithFormat:@"open device failed"]];
    }
    else {
        [self reviceData];
        [self displayTextToTerminal:[NSString stringWithFormat:@"open device:%d successful",GUIfd]];
        [self displayTextToTerminal:@"set serialPort successful"];
        [self displaySerialConfigDataToTermianl];
        [self displayTextToTerminal:[NSString stringWithFormat:@"waiting for write and read data..."]];
        self.openSerialButton.enabled = NO;
        self.closeSerialButton.enabled = YES;
        self.sendTextButton.enabled = YES;
        self.cleanInputBuffButton.enabled=YES;
        self.cleanReadBuffButton.enabled=YES;
    }
}


/*
* Close serial button.
*/
- (IBAction)closeSerial:(id)sender {
    [serialPortController closeSerialPort:GUIfd];
    GUIfd = -1;
    usleep(1000*100);
    [self displayTextToTerminal:[NSString stringWithFormat:@"colse serial device!"]];
    self.openSerialButton.enabled = YES;
    self.closeSerialButton.enabled = NO;
    self.sendTextButton.enabled = NO;
    self.cleanInputBuffButton.enabled=NO;
    self.cleanReadBuffButton.enabled=NO;
    
}


/*
* Send text button, Send the string to serial device.
*/
- (IBAction)sendText:(id)sender {
    //write data
    [self sendinnerFunc];
    
//    sleep(1);
//    //read data
//    //read data with ASCII
//    if (self.readASCIICheckbutton.state == YES) {
//        [self readDataWithASCII];
//    }
//    //read data with HEx
//    else {
//        [self readDataWithHEX];
//    }
}


-(void)sendinnerFunc{
    if ([self.sendTextButton.title isEqualToString:@"发送"]) {
        //send data with ASCII
        if (self.sendASCIICheckButton.state == YES) {
            [self sendDataWithASCII];
        }
        // send data with HEX
        else {
            [self sendDataWithHEX];
        }
    }else if([self.sendTextButton.title isEqualToString:@"循环发送"]){
        int timeIntervalInt = [timeInterval intValue];
        isLoopSending =YES;
        dispatch_queue_t queue = dispatch_queue_create("testQueue",DISPATCH_QUEUE_SERIAL);
          dispatch_async(queue, ^{
              while (self->isLoopSend && self->isLoopSending){
                  dispatch_sync(dispatch_get_main_queue(), ^(){
                      self->_sendTextButton.title=@"停止发送";
                     //send data with ASCII
                     if (self.sendASCIICheckButton.state == YES) {
                         [self sendDataWithASCII];
                     }
                     // send data with HEX
                     else {
                         [self sendDataWithHEX];
                     }
                  });
                  usleep(timeIntervalInt*1000);
              }
          });
    }else{
        //stop loop
        isLoopSending =NO;
        _sendTextButton.title=@"循环发送";
    }
}




/*
* Clean terminal button.
*/
- (IBAction)cleanTerminal:(id)sender {
    [self.terminalLog setString:@""];
    
}

/*
* Clean send text box button.
*/
- (IBAction)cleanSendText:(id)sender {
    [self.sendTextView setStringValue:@""];

}


/*
*
* Send data in ASCII mode.
*/
- (IBAction)sendASCIIcheck:(id)sender {
    self.sendASCIICheckButton.state=YES;
    self.sendHEXCheckButton.state = NO;
    [self displayTextToTerminal:@"send data with ASCII"];
    
}


/*
* Send data in HEX mode.
*/
- (IBAction)sendHEXcheck:(id)sender {
    self.sendASCIICheckButton.state=NO;
    self.sendHEXCheckButton.state = YES;
    [self displayTextToTerminal:@"send data with HEX"];
}


/*
* Clean write input buff.
 tcflush() 丢弃要写入引用的对象，但是尚未传输的数据，或者收到但是尚未读取的数据，取决于 queue_selector 的值：
 　　TCIFLUSH 　 刷新收到的数据但是不读
 　　TCOFLUSH 　 刷新写入的数据但是不传送
 　　TCIOFLUSH 　同时刷新收到的数据但是不读，并且刷新写入的数据但是不传送
*/
- (IBAction)cleanInputBuff:(id)sender {
    tcflush(GUIfd,TCIFLUSH);
    [self displayTextToTerminal:@"clean write input buff!"];
}


/*
* Read data in ASCII mode.
*/
- (IBAction)readASCIIcheck:(id)sender {
    self.readASCIICheckbutton.state = YES;
    self.readHEXCheckButton.state = NO;
    [self displayTextToTerminal:@"read data with ASCII"];
}


/*
* Read data in HEX mode.
*/
- (IBAction)readHEXcheck:(id)sender {
    self.readASCIICheckbutton.state = NO;
    self.readHEXCheckButton.state = YES;
    [self displayTextToTerminal:@"read data with HEX"];
}


/*
* Clean read output buff.
*/
- (IBAction)cleanReadBuff:(id)sender {
    tcflush(GUIfd, TCOFLUSH );
    [self displayTextToTerminal:@"clean read output buff!"];
}



- (IBAction)autoSaveLog:(id)sender {
    autoSaveFlag = self.autoSavelogBtn.state;
}

- (IBAction)saveLog:(id)sender {
    [self saveFile];
}

- (IBAction)addEndMark:(id)sender {
    isSendEndMarkStr = self.addEndMarkBtn.state;
    NSString*str =[NSString stringWithFormat:@"is send end mark:%@",isSendEndMarkStr?@"YES":@"NO"];
    NSLog(@"%@",str);
    [self displayTextToTerminal:str];
    if (isSendEndMarkStr ==YES) {
        endMarkStr = self.endMarkCK.selectedItem.title;
    }
}

- (IBAction)choiceEndMark:(id)sender {
    endMarkStr = self.endMarkCK.selectedItem.title;
    NSLog(@"end mark:%@",endMarkStr);
    NSString*str =[NSString stringWithFormat:@"choice end mark:%@",endMarkStr];
    NSLog(@"%@",str);
    [self displayTextToTerminal:str];
}


- (IBAction)timerBtnCK:(id)sender {
    isLoopSend = self.timerBtn.state;
    [self displayTextToTerminal:[NSString stringWithFormat:@"is loop send:%@",isLoopSend?@"YES":@"NO"]];
    if (isLoopSend) {
        timeInterval = self.timerText.stringValue;
        [self displayTextToTerminal:[NSString stringWithFormat:@"set timeInterval :%@",timeInterval]];
        self.sendTextButton.title =@"循环发送";
    }else{
        self.sendTextButton.title =@"发送";
    }
    
    
}


/* ===============================***self function***================================= */

/*
* load serial config to GUI view.
*/
-(void)loadingConfig{
    //init SWSerialPortController
    serialPortController=[[SWSerialPortController alloc]init];
    
    // load and check serial param and config data
    checkedData=[serialPortController loadingConfigFile];
    
    //set value to GUI selector view from checkedData attribute
    self.serialDevicePath.stringValue = [checkedData devPath];
    self.baudRateSelector.stringValue = [NSString stringWithFormat:@"%d",[checkedData baudRate]];
    self.dataBitSelector.stringValue = [NSString stringWithFormat:@"%d",[checkedData dataBit]];
    self.stopBitSelector.stringValue = [NSString stringWithFormat:@"%d",[checkedData stopBit]];
    self.paritySelector.stringValue = [checkedData parity];
    
    // display serial load info to terminal
    [self displayTextToTerminal:[NSString stringWithFormat:@"loading config file..."]];
    [self displaySerialConfigDataToTermianl];
}


/*
* Scan available device path and set it of the value to eeviceSelector view
*/
-(void)scanDevicePath{
    [self.serialDevicePath removeAllItems];
    [self.serialDevicePath addItemsWithObjectValues:[serialPortController deviceArray]];
}



/*
* set the value to selector view
*/
- (void)setBoxParam{
    
    [self.baudRateSelector removeAllItems];
    [self.baudRateSelector addItemsWithObjectValues:[checkedData baudRateArray]];
    
 
    [self.dataBitSelector removeAllItems];
    [self.dataBitSelector addItemsWithObjectValues:[checkedData dataBitArray]];
    
    
    [self.stopBitSelector removeAllItems];
    [self.stopBitSelector addItemsWithObjectValues:[checkedData stopBitArray]];
    

    [self.paritySelector removeAllItems];
    [self.paritySelector addItemsWithObjectValues:[checkedData parityArray]];
}


/*
* init GUI config.
*/
-(void)initGUIConfig
{
    GUIfd=-1;
    self.openSerialButton.enabled=YES;
    self.closeSerialButton.enabled=NO;
    self.sendTextButton.enabled=NO;
    self.cleanInputBuffButton.enabled=NO;
    self.cleanReadBuffButton.enabled=NO;
    self.autoSavelogBtn.state =YES;
    autoSaveFlag = YES;
    self.addEndMarkBtn.state =YES;
    isSendEndMarkStr =YES;
    self.addEndMarkBtn.enabled =YES;
    [self.endMarkCK removeAllItems];
    [self.endMarkCK addItemsWithTitles:@[@"LF(\\n)",@"BCRLF(\\r\\n)",@"CR(\\r)"]];
    [self.endMarkCK setTitle:@"CR(\\r)"];
    endMarkStr = @"CR(\\r)";
    
    self.timerText.stringValue =@"1000";
    self.timerBtn.state =NO;
    isLoopSend =NO;
    timeInterval =@"1000";
    isLoopSending =NO;

    basePath = [NSString stringWithFormat:@"/vault/Serialtool/%@",[ZTDateHandle formatTimeString:@"yyyy_MM_dd_HH_mm_ss"]];

    center =[NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(saveLogNoti:) name:@"autoSave" object:nil];
    
    //load config file
    [self loadingConfig];
    [self scanDevicePath];
    [self setBoxParam];
  
}


/* date */
- (NSString *)date {
    static NSDateFormatter *formatter;
    if (formatter == nil) {
        formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"[yyyy-MM-dd HH:mm:ss.SSS]"];
    }
    return [formatter stringFromDate:[NSDate date]];
}

/* 选择文件方法 */
-(NSString*)findFile{
    NSString*path=@"";
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    [panel setMessage:@"Open File"]; //提示信息
    [panel setCanChooseFiles:YES];//是否能选择文件file
    [panel setCanChooseDirectories:YES];//是否能打开文件夹
    [panel setAllowsMultipleSelection:NO];//是否允许多选file
    [panel setCanCreateDirectories:YES]; //是否可以创建文件夹
    [panel setDirectoryURL:[NSURL URLWithString:@"/Users/mt-sw-005/Desktop/ZHYY/TTtest/"]]; //设置默认打开的文件夹

    NSInteger finded = [panel runModal]; //获取panel的响应
    if (finded == NSModalResponseOK) {
        path =[panel.URL path];
    }

    return path;
}


-(void)saveFile{
    NSSavePanel *savePanel =[NSSavePanel savePanel];
    savePanel.message = @"Save File";
    savePanel.title = @"Save File Panel";
    savePanel.nameFieldStringValue=@"new File";
    savePanel.canCreateDirectories=YES;
    savePanel.canSelectHiddenExtension=YES;
    savePanel.directoryURL =[NSURL URLWithString:@"~/Desktop"];
    
    NSInteger finded =[savePanel runModal];
    if (finded == NSModalResponseOK) {
        NSString *filePath = [[savePanel URL] path];
        [ZTFileHandle createFile:filePath];
        NSString *logstr  =_terminalLog.string;
        [ZTFileHandle writeStringLog:logstr toLogFile:filePath];
        NSLog(@"--->%@<-----------------\n",filePath);
    }
    
}



- (void)saveLogNoti:(NSNotification *)noti{
 
    //这个方法的参数就是发送通知postNotification:方法的参数发送过来额通知。当要使用传递的userInfo的时候，就要使用noti解析出userInfo中需要的字段
    NSString *message =[noti.userInfo objectForKey:@"message"];
//    NSLog(@"=====|||%@",message);
    if (autoSaveFlag) {
        autoSavePath =[ZTFileHandle createDirectory:basePath];
        NSString *logpath =[ZTFileHandle createFileWithPath: autoSavePath FileName:@"serial.log"];
        [ZTFileHandle writeStringLog:message toLogFile:logpath];
    }
}

/*
* Display serial log info to GUI termianl view.
*/
-(void)displayTextToTerminal:(NSString *)message
{
    NSString *msg = [NSString stringWithFormat:@"%@ %@",[self date],message];
    //发送通知给saveLogNoti
    NSNotification *notification=[NSNotification notificationWithName:@"autoSave"  object:nil userInfo:@{@"message":msg}];
    [center postNotification:notification];
   
    NSColor *color;
    if ([[message uppercaseString] containsString:@"ERROR"]|[[message uppercaseString] containsString:@"FAIL"]) {
        color = [NSColor redColor];
    }else if ([[message uppercaseString] containsString:@"OK"]|[[message uppercaseString] containsString:@"SUCCESS"]){
        color =[NSColor greenColor];
    }else{
        color =[NSColor blackColor];
    }
    
    NSString *messageWithNewLine = [msg stringByAppendingString:@"\n"];

    // Smart Scrolling
    BOOL scroll = (NSMaxY(self.terminalLog.visibleRect) == NSMaxY(self.terminalLog.bounds));

    // Append string to textview
    [self.terminalLog.textStorage appendAttributedString:[[NSAttributedString alloc]initWithString:messageWithNewLine attributes:@{NSForegroundColorAttributeName:color}]];

    if (scroll) // Scroll to end of the textview contents
        [self.terminalLog scrollRangeToVisible: NSMakeRange(self.terminalLog.string.length, 0)];
}



/*
* display config data info to terminal view.
*/
- (void)displaySerialConfigDataToTermianl {
    [self displayTextToTerminal:[NSString stringWithFormat:@"device path: %@\nbaudRate: %@\ndataBit: %@\nstopBit: %@\nparity: %@",self.serialDevicePath.stringValue,
              self.baudRateSelector.stringValue,
              self.dataBitSelector.stringValue,
              self.stopBitSelector.stringValue,
              self.paritySelector.stringValue]];
    
}




/*
* Send data with ASCII.
*/
-(void)sendDataWithASCII {
    NSMutableString *str = [NSMutableString stringWithString: self.sendTextView.stringValue];
    NSString *logStr =[NSString stringWithString:str];
    if (isSendEndMarkStr ==YES) {
        NSLog(@"=======end mark :%@",endMarkStr);
        if ([endMarkStr hasPrefix:@"CR"]) {
            [str appendString:@"\r"];
        }else if ([endMarkStr hasPrefix:@"LF"]) {
            [str appendString:@"\n"];
        }else if ([endMarkStr hasPrefix:@"BCRLF"]) {
            [str appendString:@"\r\n"];
        }
    }
    ssize_t writeLength = [serialPortController writeData:GUIfd withASCIIStr:str];
    NSString *text = [NSString stringWithFormat:@"write data: >>>【%@】 is %@",logStr,writeLength?@"success":@"failed"];
    [self displayTextToTerminal:text];
    usleep(1000*100);
}



/*
* Send data with HEX.
*/
-(void)sendDataWithHEX {
    [self displayTextToTerminal:@"send data with HEX is not available"];
    NSMutableString *str = [NSMutableString stringWithString: self.sendTextView.stringValue];
    NSString *logStr =[NSString stringWithString:str];
    if (isSendEndMarkStr) {
        if ([endMarkStr hasPrefix:@"CR"]) {
            [str appendString:@"\r"];
        }else if ([endMarkStr hasPrefix:@"LF"]) {
            [str appendString:@"\n"];
        }else if ([endMarkStr hasPrefix:@"BCRLF"]) {
            [str appendString:@"\r\n"];
        }
    }
  size_t writeLength = [serialPortController writeData:GUIfd withHEXStr:str];
    NSString *text = [NSString stringWithFormat:@"write data: >>> 【%@】  is %@",logStr,writeLength?@"success":@"failed"];
    [self displayTextToTerminal:text];
    usleep(1000*100);
}



/*
* Read data with ASCII.
*/
-(void)readDataWithASCII {
    NSDictionary *readDataDict = [serialPortController readData:GUIfd];
    if ( ([readDataDict[@"readDataLength"] intValue]) == 0) {
        [self displayTextToTerminal:@"read result : <<< read data failed"];
    }
    else{
        [self displayTextToTerminal:[NSString stringWithFormat:@"read result : <<<\n%@",(readDataDict[@"readData"])]];
    }
}


/*
* Read data with Hex.
*/
-(void)readDataWithHEX {
    [self displayTextToTerminal:@"read data with HEX  is not available"];
    NSDictionary *readDataDict = [serialPortController readData:GUIfd];
    if ( ([readDataDict[@"readDataLength"] intValue]) == 0) {
        [self displayTextToTerminal:@"read result : <<< read data failed"];
    }
    else{
        char *readStr=(char *)([readDataDict[@"readData"] UTF8String]);
        NSLog(@"read char str:%s",readStr);
        char *hexStr=[serialPortController chsToHex:readStr];
        [self displayTextToTerminal:[NSString stringWithFormat:@"read result : <<<\n%s",hexStr]];
    }
    
}


-(void)reviceData {
    dispatch_queue_t queue = dispatch_queue_create("testQueue",DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        while (self->GUIfd > 0) {
            dispatch_sync(dispatch_get_main_queue(), ^(){
                NSString *readStr = [self readData];
//                NSLog(@"========||||||||||||%@",readStr);
                if ([readStr containsString:@"NONE"]) {
                    readStr =@"";
                }else{
                    [self displayTextToTerminal:[NSString stringWithFormat:@"read  data: <<<\n%@\n",readStr]];
                }
            });
        }
    });
}



-(void)autoSearchDevice {
    dispatch_queue_t queue = dispatch_queue_create("testQueue",DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        while (YES) {
            usleep(1000*1000);
            dispatch_sync(dispatch_get_main_queue(), ^(){
               [self scanDevicePath];
            });
        }
    });
}



-(NSString*)readData{
    //read data
    //read data with ASCII
    NSString *readStr;
    if (self.readASCIICheckbutton.state == YES) {
        NSDictionary *readDataDict = [serialPortController readData:GUIfd];
        if ( ([readDataDict[@"readDataLength"] intValue]) == 0) {
            readStr =@"NONE";
        }
        else{
            readStr = [NSString stringWithFormat:@"%@",readDataDict[@"readData"]];
        }
    }
    //read data with HEx
    else {
        NSDictionary *readDataDict = [serialPortController readData:GUIfd];
        if ( ([readDataDict[@"readDataLength"] intValue]) == 0) {
            readStr =@"NONE";
        }
        else{
            char *str=(char *)([readDataDict[@"readData"] UTF8String]);
            NSLog(@"read char str:%s",str);
            char *hexStr=[serialPortController chsToHex:str];
            readStr = [NSString stringWithFormat:@"%s",hexStr];
        }
    }
    return  readStr;
}
@end
